Day29 要來做個倒數計時器
function useTimer(initialTime: number): TimerHook {
const [time, setTime] = useState<number>(initialTime);
const [isRunning, setIsRunning] = useState<boolean>(false);
const [endTime, setEndTime] = useState<Date | null>(null);
const timerRef = useRef<number | null>(null);
const startTimer = useCallback((seconds: number) => {
// Clear any existing interval
if (timerRef.current) {
clearInterval(timerRef.current);
}
const now = Date.now();
const then = now + seconds * 1000;
setEndTime(new Date(then));
setIsRunning(true);
setTime(seconds);
timerRef.current = window.setInterval(() => {
setTime((prevTime) => {
if (prevTime <= 1) {
if (timerRef.current) clearInterval(timerRef.current);
setIsRunning(false);
return 0;
}
return prevTime - 1;
});
}, 1000);
}, []);
const stopTimer = useCallback(() => {
if (timerRef.current) {
clearInterval(timerRef.current);
}
setIsRunning(false);
}, []);
return { time, isRunning, startTimer, stopTimer, endTime };
}
type PresetButton = {
time: number;
label: string;
};
const presetButtons: PresetButton[] = [
{ time: 20, label: "20 Secs" },
{ time: 300, label: "Work 5" },
{ time: 900, label: "Quick 15" },
{ time: 1200, label: "Snack 20" },
{ time: 3600, label: "Lunch Break" },
];
const { time, startTimer, endTime } = useTimer(0);
const formatEndTime = (date: Date | null): string => {
if (!date) {
return "";
}
return `Be back at ${date.getHours()}:${date
.getMinutes()
.toString()
.padStart(2, "0")}`;
};
const handleCustomTime = (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
const form = event.currentTarget;
const minutesInput = form.elements.namedItem("minutes") as HTMLInputElement;
const minutes = parseInt(minutesInput.value, 10);
if (!isNaN(minutes)) {
startTimer(minutes * 60);
}
form.reset();
};
return (
<div className="bg-gradient-to-br from-blue-400 via-blue-500 to-blue-900 min-h-screen text-center">
<div className="flex flex-col min-h-screen">
<div className="flex">
{presetButtons.map((preset) => (
<button
key={preset.time}
type="button"
onClick={() => startTimer(preset.time)}
className="flex-1 bg-black bg-opacity-10 text-white text-2xl uppercase py-4 border-b-3 border-black border-opacity-20 border-r hover:bg-opacity-20 focus:outline-none"
>
{preset.label}
</button>
))}
<form onSubmit={handleCustomTime} className="flex-1 flex">
<input
type="text"
name="minutes"
placeholder="Enter Minutes"
className="flex-1 bg-transparent border-0 text-2xl text-white placeholder-white placeholder-opacity-50 p-4 focus:outline-none"
/>
</form>
</div>
<div className="flex-1 flex flex-col items-center justify-center">
<h1 className="text-white text-[12rem] font-bold m-0 leading-none">
{formatTime(time)}
</h1>
<p className="text-white text-4xl">{formatEndTime(endTime)}</p>
</div>
</div>
</div>
);